package com.cht.commonUtil.checkIn;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.tmatesoft.svn.core.ISVNDirEntryHandler;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNCopyClient;
import org.tmatesoft.svn.core.wc.SVNCopySource;
import org.tmatesoft.svn.core.wc.SVNLogClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import com.cht.commonUtil.structure.I1S1;
import com.cht.commonUtil.webInit.SP;
import com.cht.commonUtil.enums.ChangeType;
import com.cht.commonUtil.enums.CheckInStatus;
import com.cht.commonUtil.enums.MailType;
import com.cht.commonUtil.enums.RecipientType;
import com.cht.commonUtil.widgets.CommitMediator;
import com.cht.commonUtil.widgets.Widgets;

public class ApproveChkIn extends HttpServlet {
	private static final long serialVersionUID = 1L;
	List<I1S1> fileList = new ArrayList<I1S1>();

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request,response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		request.setCharacterEncoding( SP.UIcodeSet );
		response.setCharacterEncoding( SP.UIcodeSet );
		response.setContentType("application/text");
		
		/**
		 * 檢查這筆資料是不是還在(沒有其它主管先回覆了) 
		 * 將檔案從 SVN AP 寫入 SVN CM OL
		 * 計算此模組所推導的最後部署模組有那些，還有中間經過的點，以及上述各點的預期Build版的job版數
		 * 取得Build版封裝的進度以寫入DB
		 * 改變繳交的狀態
		 * 刪除todolist
		 * 寄發通知信(開發人員、CM人員、測試人員)
		 **/
		
		boolean isApprove = Integer.parseInt(request.getParameter("isApprove"))==1?true:false;	//主管是否同意
		int chkInMainId = Integer.parseInt(request.getParameter("chkInId"));	//繳交申請ID
		int toDoListId = Widgets.getParNumberFromRequest( request.getParameter("toDoListId") );
		String subsysId = request.getParameter("subsysId");
		String committer = request.getParameter("creatorId");
		String empId = request.getParameter("empId");
		String empName = request.getParameter("empName");
		
		int changeTypeId = ChangeType.REJECT_CHKIN;
		int chkInStatusId = CheckInStatus.REJECT_APPLY;
		if ( isApprove ) {
			changeTypeId = ChangeType.APPROVE_CHKIN;
			chkInStatusId = CheckInStatus.CHKIN2SVNing_SOURCE;
		}
		
		
		String sql;
		String res = null;
		Connection conn = null;
		PreparedStatement ps = null;
		PreparedStatement ps2 = null;
		ResultSet rs = null;
		ResultSet rs2 = null;
		
		//SVN抓取檔案要用
		InputStream ins = null;
		ByteArrayOutputStream baos = null;
		SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
		SVNClientManager manager = null;
		SVNRepository cmOLrepo = null;	//SVN CM OL的Repo
		SVNRepository apRepo = null;	//SVN AP 的repo
		SVNCopySource cs = null;		
		try {
			
			conn = Widgets.getConn();
			
			/****檢查有沒有其它主管先回覆了****/
			sql="SELECT chkInStatusId FROM chkinmain WHERE chkInMainId=?";
			ps = conn.prepareStatement(sql);
			ps.setInt(1, chkInMainId);
			rs = ps.executeQuery();
			rs.next();
			if ( rs.getInt(1)==1 ) {	//表示仍處於待審的狀態
				if ( rs !=null ) {rs.close();}
				if ( ps !=null ) {ps.close();}
				
				/****建立LOG，第一步就開始記錄程式的執行****/
				sql="INSERT INTO changelog (changeLogId,changeTypeId,creatorId,bDate) VALUES (null,"+changeTypeId+",?,?)";
				ps = conn.prepareStatement(sql, new String[]{"changeLogId"} );
				ps.setString(1, empId);
				ps.setTimestamp(2, Widgets.cDT());
				ps.execute();
				rs = ps.getGeneratedKeys();
				rs.next();
				final int chkInId4Approve = rs.getInt(1);
				
				//修改繳交記錄的狀態
				Widgets.updateCHKINstatus(chkInMainId, chkInStatusId);
				
				
				//刪除已完成的待辦事項，將其加入待辦清單
				Widgets.completedList(toDoListId, committer);
		
				sql="SELECT t3.subsysDesc,t1.svnOriPath,t1.svnOLurl,t1.apOriVer,t2.empName FROM chkinmain t1,emps t2,subsyses t3 WHERE t1.subsysId=t3.subsysId AND t1.creatorId=t2.empId AND t1.chkInMainId=?";
				ps = conn.prepareStatement(sql);
				ps.setInt(1, chkInMainId);
				rs = ps.executeQuery();
				rs.next();
				String svnUrl = rs.getString("svnOLurl");
				String svnOriPath = rs.getString("svnOriPath");
				String subsysDesc = rs.getString("subsysDesc");
				String svnFinalPath = svnOriPath;	//當選定的資料夾是 branch下的夾，此時就會相等
				long apOriVer = rs.getInt("apOriVer");
				long apFinalVer = apOriVer;	//最後送出去的版本 (如果選的資料夾是 branch下的，就不會有錯
				
				String msgBody = "<font face='微軟正黑體'>您好,<p>您的主管"+empName+"已經"+(isApprove?"同意":"駁回")+"了您繳交原始碼("+chkInMainId+")的申請<p> ";	//信件內容
				msgBody += "原始碼來源："+svnUrl+svnOriPath+"/<br>";
				msgBody += "版次："+apOriVer+"<P>";
				String reqs = "<ul class='zhTwN req'>";
				sql = "SELECT t1.reqSID,t1.reqDesc FROM reqs t1,mpChkinReq t2 WHERE t1.reqId=t2.reqId AND t2.chkInMainId=?";
				ps = conn.prepareStatement(sql);
				ps.setInt(1, chkInMainId);
				rs = ps.executeQuery();
				while( rs.next() ) {
					reqs += "<li>"+rs.getString("reqSID")+"-"+rs.getString("reqDesc")+"</li>";
				}
				reqs += "</ul>";
				msgBody += "相關需求："+reqs+"<p><p>Best regards<br>"+empName+"</font><p><p>";
				
				
				if ( isApprove ) {
					
					
					/*
					 * 1. 選擇的資料夾若是為 trunk
					 * 		將其複制到 branch下
					 * 2. 修改繳交記錄中，修改後欄位的值
					 * 3. 將 後欄位 中的內容，複制到 CM OL下
					 * 4. 驅動 jenkins的job
					 * */
					
					
					//將檔案從 SVN AP 寫入 SVN CM OL
					try {
						
						ISVNEditor editor = null;
						SVNCommitInfo info = null;
						
						/*	此區開始建立SVN連線	*/
						List<String> connCMsvnOLinfo = Widgets.getSVN4SubsysConnInfo(subsysId, 2);	//SVN CM OL的連線資訊
						List<String> svnAP = Widgets.getSVN4SubsysConnInfo(subsysId, 4);	//SVN AP OL的連線資訊
						
						ISVNAuthenticationManager authManager = null;
						apRepo = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(svnAP.get(0)));	//連線到SVN AP OL 的repo
				        authManager = SVNWCUtil.createDefaultAuthenticationManager(svnAP.get(1), svnAP.get(2));
				        apRepo.setAuthenticationManager(authManager);
				        //String commitMessage = apRepo.info(svnOriPath, apOriVer).getCommitMessage();
				        String commitMessage = "";
				        editor = apRepo.getCommitEditor(null, new CommitMediator() );
			            editor.openRoot(-1);
				        

			            
			        	if ( "trunk".equalsIgnoreCase(svnOriPath)) {
			            	//以今天為日期，建立資料夾
			            	SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_");
			        		String today = formatter.format( new Date() );
			        		editor.openDir("branch", -1);
			        		int i=1;
			        		while ( true ) {
			        			
			        			String folder = String.format(today+"%02d", i++);	//預計在 ap repo中創建的夾名
			        			
			        	        try {	//若能夠打開該路徑的資料夾，便是該夾存在，否則便進入catch來創建該資料夾
			        	        	System.out.println("Before add:"+folder);
			        	        	editor.addDir(folder, null, -1);
			        	        	System.out.println("After add:"+folder);
			                		editor.closeDir();
			                		svnFinalPath = "branch/"+folder;
			                		break;
			                	} catch ( Exception e) {
			                		editor.closeDir();
			                	}
			        		}
			        		SVNCopyClient copyClient = new SVNCopyClient(authManager, new DefaultSVNOptions() );
			            	cs = new SVNCopySource(SVNRevision.HEAD,SVNRevision.HEAD,SVNURL.parseURIEncoded( svnAP.get(0)+"trunk" ));
			            	
			            	SVNCopySource[] sources = new SVNCopySource[1];
			            	sources[0] = cs;
			            	info = copyClient.doCopy(sources, SVNURL.parseURIEncoded( svnAP.get(0)+svnFinalPath ), false, false, false, "程式創建", new SVNProperties());
			            	apFinalVer = info.getNewRevision();
			            	
			    		}
			        	//將最後要取程式碼的路徑給記錄下來
			        	sql = "UPDATE chkinmain SET svnFinalPath=?,apFinalVer=? WHERE chkInMainId=?";
			        	ps = conn.prepareStatement(sql);
			        	ps.setString(1, svnFinalPath);
			        	ps.setLong(2, apFinalVer);
			        	ps.setInt(3, chkInMainId);
			        	ps.execute();
			        	
			        	//開始將檔案從 SVN AP 複制到 SVN 
			        	manager = SVNClientManager.newInstance(new DefaultSVNOptions(), svnAP.get(1), svnAP.get(2)); // 如果需要用戶名密碼
						SVNLogClient logClient = manager.getLogClient();
						ISVNDirEntryHandler handler = new FileEntryHandler();
						
						fileList.clear();
						
						logClient.doList(SVNURL.parseURIEncoded(svnAP.get(0)+svnFinalPath), SVNRevision.HEAD, SVNRevision.HEAD,false, true, handler); // 列出當前svn地址的目錄，對每個文件進行處理
						
						System.out.println("原始碼要來自："+svnAP.get(0) );
						System.out.println("原始碼要簽入："+connCMsvnOLinfo.get(0) );
						SVNRepository cmRepo = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(connCMsvnOLinfo.get(0)));
						authManager = SVNWCUtil.createDefaultAuthenticationManager(connCMsvnOLinfo.get(1), connCMsvnOLinfo.get(2));
				        cmRepo.setAuthenticationManager(authManager);
				        
				        if ( apRepo != null ) { apRepo.closeSession(); }
				        apRepo = SVNRepositoryFactory.create( SVNURL.parseURIEncoded(svnAP.get(0)+svnFinalPath) );	//連線到SVN AP OL 的repo
				        authManager = SVNWCUtil.createDefaultAuthenticationManager(svnAP.get(1), svnAP.get(2));
				        apRepo.setAuthenticationManager(authManager);
				       
				        //我需要的COMMIT 還沒有簽進去
			            editor = cmRepo.getCommitEditor(commitMessage, new CommitMediator() );
			            editor.openRoot(-1);
			            SVNProperties properties = new SVNProperties();
			            
			            for(int i=0; i<fileList.size();i++) {
			            	I1S1 is = fileList.get(i);
			            	String cmPath = "";
			            	String[] pathArray = is.getS1().split("/");	//記錄這個檔案延途的路徑
			            	int numberCloseDir = pathArray.length; //這是給SVN CM用的，算階層的(目前這行是先以Dir來看)，用以回到根節點
			            	System.out.println("次數："+numberCloseDir);
			            	if( is.getI1() == 0 ) {	//表示是資料夾
			            		System.out.println("這個是資料夾："+is.getS1());
			            		for(int j=0; j<numberCloseDir ;j++ ) {
			            			cmPath += pathArray[j]+"/";	//這是給 SVN AP用的，抓資料的路徑
			            			String path = cmPath.substring(0, cmPath.length()-1);
			            			System.out.println("目前處理："+path);
			            			try {
			            				editor.addDir( path, null , -1 );
			            	        	System.out.println("路徑："+path+"，不存在==>創建");
			            	        } catch(Exception e) {
			            	        	System.out.println("路徑："+path+"，存在");
			            	        }
			            		}
			            		for(int j=0; j<numberCloseDir ;j++ ) {
			            			editor.closeDir();
			            		}
			            	} else {	//表示是檔案
			            		numberCloseDir--;
			            		String fn = pathArray[numberCloseDir];
			            		System.out.println("這個是檔案："+is.getS1()+",要close的次數"+numberCloseDir);
			            		
			            		//先用切換到它所該在的路徑下
			            		for(int j=0; j<numberCloseDir ;j++ ) {
			            			cmPath += pathArray[j]+"/";	//這是給 SVN AP用的，抓資料的路徑
			            			System.out.println("目前處理："+pathArray[j]);
			            			String path = cmPath.substring(0, cmPath.length()-1);
			            			try {
			            				editor.addDir( path, null , -1);
			            	        	System.out.println("路徑："+pathArray[j]+"，不存在==>創建");
			            	        } catch(SVNException e) {
			            	        	System.out.println("路徑："+pathArray[j]+"，存在==>進入");
			            	        }
			            		}
			            		cmPath += fn;
			            		System.out.println("檔案名稱:"+fn+"\n");
			            		
			            		//重新宣告一個檔案，不然檔案會一直append下去
			            		baos = new ByteArrayOutputStream();
			            		apRepo.getFile( is.getS1(),apFinalVer, properties, baos);
			  			        ins  = new ByteArrayInputStream( baos.toByteArray() );
			            		try {
			            			editor.addFile( is.getS1(), null , -1 );
			            			System.out.println("新增了檔案"+is.getS1());
			        	        } catch(SVNException e) {
			        	        	editor.openFile( is.getS1(), -1);
			        	        	System.out.println("開啟了檔案"+is.getS1());
			        	        }
			            		editor.applyTextDelta( is.getS1(), null); 
			            		String baseChecksum = deltaGenerator.sendDelta( is.getS1(), ins, editor, true);
			    	    	    editor.textDeltaEnd( is.getS1() );
			    	    	    editor.closeFile( is.getS1(), baseChecksum );
			    	    	    
			    	    	    for(int j=0; j<numberCloseDir ;j++ ) {
			            			editor.closeDir();
			            		}
			            	}
			            }
			            
			            info = editor.closeEdit();
				        //寫回 SVN CM的版次
			    	    long cmOLrepoVer = info.getNewRevision();
			    	    
			    	    System.out.println("簽入結束，CM原始碼區的版次是："+cmOLrepoVer);
			    	    
			    	    sql = "UPDATE chkinmain SET cmFileVer=? WHERE chkInMainId=?";
			    	    ps = conn.prepareStatement(sql);
			    	    ps.setLong(1, cmOLrepoVer);
						ps.setInt(2, chkInMainId);
						ps.execute();
						
					} catch(Exception e) {
						Widgets.errLog(e);
						chkInStatusId = CheckInStatus.FAIL_CHKIN_SVN;
						Widgets.updateCHKINstatus(chkInMainId, chkInStatusId);
						throw e;
					}
					
					/**
					 * 開始處理 Build版封裝
					 * */
					//準備驅動jenkins
					chkInStatusId = CheckInStatus.READY_TRIGGER_JENKINS;
					Widgets.updateCHKINstatus(chkInMainId, chkInStatusId);
					
					System.out.println("簽入完成，準備驅動Jenkins，先更新各個job的內容");
					sql = "SELECT modId FROM mods WHERE subsysId=?";
					ps = conn.prepareStatement(sql);
					ps.setString(1, subsysId);
					rs = ps.executeQuery();
					while( rs.next() ) {
						String modId = rs.getString("modId");
						Widgets.updateJenkinsJobPostBuildURL(committer,modId, chkInMainId, subsysId, true);
						System.out.println("修改完了"+ modId );
					}
					
					Widgets.triggerJenkinsBuild( "BuildSaiTo" );
					
				}
				
				//寄發通知信
				List<String[]> tos = Widgets.getRecipient((isApprove?MailType.APPROVE_CHKIN_AGREE:MailType.APPROVE_CHKIN_DISAGREE), RecipientType.TO, subsysId);
				List<String[]> ccs = Widgets.getRecipient((isApprove?MailType.APPROVE_CHKIN_AGREE:MailType.APPROVE_CHKIN_DISAGREE), RecipientType.CC, subsysId);
				List<String[]> bccs = Widgets.getRecipient((isApprove?MailType.APPROVE_CHKIN_AGREE:MailType.APPROVE_CHKIN_DISAGREE), RecipientType.BCC, subsysId);
				String subject = (isApprove?"同意":"駁回")+"-"+subsysDesc+"-繳交申請回覆-"+chkInMainId;
				Widgets.sendMailMain(tos,ccs,bccs,subject,msgBody);				
				
				//寄發通知信
				
				/****完成這次的異動****/
				sql = "UPDATE changelog SET eDate=? WHERE changeLogId=?";
				ps = conn.prepareStatement(sql);
				ps.setTimestamp(1, Widgets.cDT());
				ps.setInt(2, chkInId4Approve);
				ps.execute();
				
				res="OK";
			}
		} catch(Exception e) {
			res = Widgets.errLog(e);
		} finally {
			try {
				if ( baos !=null ) {baos.close();}
				if ( ins !=null ) {ins.close();}
				if ( rs !=null ) {rs.close();}
				if ( rs2 !=null ) {rs2.close();}
				if ( ps !=null ) {ps.close();}
				if ( ps2 !=null ) {ps2.close();}
				if ( conn !=null ) {conn.close();}
				if ( cmOLrepo != null ) { cmOLrepo.closeSession(); }
				if ( apRepo != null ) { apRepo.closeSession(); }
				
				if ( manager != null ) { manager.dispose(); }
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		response.getWriter().write(res);
	
	}
	
	class FileEntryHandler implements ISVNDirEntryHandler {
		public void handleDirEntry(SVNDirEntry entry) throws SVNException {
			I1S1 is = new I1S1();
			
			if ( entry.getKind() == SVNNodeKind.DIR ) {
				if ( !"".equalsIgnoreCase(entry.getRelativePath()) ) {
					is.setI1(0);
					is.setS1( entry.getRelativePath() );
					fileList.add( is );
				}
			} else if ( entry.getKind() == SVNNodeKind.FILE ) {
				is.setI1(1);
				is.setS1( entry.getRelativePath() );
				fileList.add( is );
			}
		}
	}
}

